import java.util.Scanner;

class MyCounter{
    // 修改之前的代码
  //  public int flag = 0;
    // 改进之后的代码
   volatile  public int flag = 0;
}

public class ThreadVola {
    // 内存可见性问题
    public static void main(String[] args) {
        MyCounter myCounter = new MyCounter();

        Thread thread1 = new Thread(()->{
            // 第一个线程要做的事情就是循环读取MyCounter里面的flag的值
            while (myCounter.flag == 0){
                // 此处的循环体咱们就空着    // 在while循环这里呢 使用汇编语言来理解大概就是两步操作： 1.load，把内存中的 flag 的值，读取到寄存器里
                //         2. cmp， 把寄存器的值 和0 经行比较。根据比较结果，决定下一步往哪个地方执行（条件跳转指令）
                //  上述操作是个循环
                // 循环执行这么多次， 在thread2真正修改之前(thread2不指定啥时候修改成功)，load得到的结果都是一样的
                // 另一方面load操作和cmp操作相比，速度要慢非常非常多
                // （cpu 针对寄存器的操作，要比内存操作快很多，要快3-4个数量级）
                // 而在java中呢 像上述的操作，由于load执行的素的太慢了（相比于cmp来说），再加上反复load 得到的结果都一样，这时JVM就做出了一个非常大胆的决定
                // 不在重复 load 了，判定好像没人修改 flag 的值了，所以干脆只读取一次就行了
                // 这时呢这样的情况就属于 编译器优化的一种方式


                // 解决方法二：
                // 当我们不在变量那边加volatile的时候可以在这里面进行休眠
/*                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }*/
            }
            System.out.println("thread1 结束");
        });

        Thread thread2 = new Thread(()->{
            // 线程二要做的事情就是写一个数赋值给flag
            Scanner scanner = new Scanner(System.in);
            System.out.println("请输入一个整数： ");
            myCounter.flag = scanner.nextInt();
        });

        // 所以会出现 线程2 输入一个不为0的数 的时候 会输出： thread1 结束
        // thread1 线程要循环快速的重复去读取， thread2 来进行修改
        thread1.start();
        thread2.start();

        // 这时候在运行之后，当我们输入 1 的时候 thread1 这个线程 并没有结束循环 ， 同时也可以看到thread2 这个线程已经没有了，
        // 执行完了。 thread1 线程显然还在继续循环

        // 我们看到的这个情况就是内存可见性问题(一个线程针对变量进行读取操作，同时另一个线程针对这个变量进行修改，此时读到的值，不一定是修改后的值
        // 。这个读线程没有感知到变量的变化，归根结底是编译器/jvm 在多线程环境下优化时产生了误判了，此时就需要程序员手动干预了，就给flag这个变量
        // 加上 volatile 关键字。意思就是告诉编译器这个变量时“易变”的)
        // 所以这个代码也是个bug ， 一个线程读 一个线程改也是会导致线程安全问题的
        // 加上干预之后呢 就得到了我们预期的结果

    }
}
